package cn.mw.cmdb.util;

import cn.mw.cmdb.enums.BasicTypeEnum;
import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import static cn.mw.cmdb.util.ValConvertUtil.*;


@Slf4j
public class ListSortUtil<T> {
    private String ascSortModel = "ASC";

    /**
     * @param list      需要排序的集合
     * @param sortField 要排序的集合中的实体类的某个字段
     * @param sortMode  排序的方式（升序0/降序1）
     */
    public List<T> sort(List<T> list, String sortField, String sortMode) {
        if (list == null || list.size() < 2) {
            return list;
        }
        //首字母转大写
        String newStr = sortField.substring(0, 1).toUpperCase() + sortField.substring(1);

        Collections.sort(list, new Comparator<T>() {
            @Override
            public int compare(T o1, T o2) {
                try {
                    Class aClass = o1.getClass();
                    Field field = null;
                    try {
                        field = aClass.getDeclaredField(sortField);//获取成员变量
                    } catch (NoSuchFieldException e) {
                        field = aClass.getSuperclass().getDeclaredField(sortField);//获取父类成员变量
                    }
                    field.setAccessible(true);//设置成可访问状态
                    String typeName = field.getType().getName().toUpperCase();//转换成大写
                    Object v1 = field.get(o1);
                    Object v2 = field.get(o2);

                    boolean ASC_order = (ascSortModel.equals(sortMode));
                    typeName = typeName.substring(typeName.lastIndexOf('.') + 1);
                    BasicTypeEnum value = BasicTypeEnum.valueOf(typeName);
                    if (null == v1) {
                        return 1;
                    }
                    if (null == v2) {
                        return -1;
                    }
                    switch (value) {
                        case INT:
                        case INTEGER:
                            Integer integer1 = Integer.parseInt(v1.toString());
                            Integer integer2 = Integer.parseInt(v2.toString());
                            return ASC_order ? integer1.compareTo(integer2) : integer2.compareTo(integer1);
                        case BYTE:
                            Byte byte1 = Byte.parseByte(v1.toString());
                            Byte byte2 = Byte.parseByte(v2.toString());
                            return ASC_order ? byte1.compareTo(byte2) : byte2.compareTo(byte1);
                        case CHAR:
                            Integer char1 = (int) (v1.toString().charAt(0));
                            Integer char2 = (int) (v2.toString().charAt(0));
                            return ASC_order ? char1.compareTo(char2) : char2.compareTo(char1);
                        case DATE:
                            Date date1 = (Date) (v1);
                            Date date2 = (Date) (v2);
                            return ASC_order ? date1.compareTo(date2) : date2.compareTo(date1);
                        case LONG:
                            Long long1 = Long.parseLong(v1.toString());
                            Long long2 = Long.parseLong(v2.toString());
                            return ASC_order ? long1.compareTo(long2) : long2.compareTo(long1);
                        case FLOAT:
                            Float float1 = Float.parseFloat(v1.toString());
                            Float float2 = Float.parseFloat(v2.toString());
                            return ASC_order ? float1.compareTo(float2) : float2.compareTo(float1);
                        case SHORT:
                            Short short1 = Short.parseShort(v1.toString());
                            Short short2 = Short.parseShort(v2.toString());
                            return ASC_order ? short1.compareTo(short2) : short2.compareTo(short1);
                        case DOUBLE:
                            Double double1 = Double.parseDouble(v1.toString());
                            Double double2 = Double.parseDouble(v2.toString());
                            return ASC_order ? double1.compareTo(double2) : double2.compareTo(double1);
                        case STRING:
                        case OBJECT:
                            String string1 = v1.toString();
                            String string2 = v2.toString();
                            if (objConvertDouble(v1) && objConvertDouble(v2)) {
                                Double num1 = Double.parseDouble(string1);
                                Double num2 = Double.parseDouble(string2);
                                return ASC_order ? num1.compareTo(num2) : num2.compareTo(num1);
                            } else if (objConvertLong(v1) && objConvertLong(v2)) {
                                Long num1 = Long.parseLong(string1);
                                Long num2 = Long.parseLong(string2);
                                return ASC_order ? num1.compareTo(num2) : num2.compareTo(num1);
                            } else if (objConvertString(v1) && objConvertString(v2)) {
                                return ASC_order ? string1.compareTo(string2) : string2.compareTo(string1);
                            } else if (v1 instanceof String) {
                                return 1;
                            } else if (v2 instanceof String) {
                                return -1;
                            } else {
                                return 0;
                            }
                        case BOOLEAN:
                            Boolean boolean1 = Boolean.parseBoolean(v1.toString());
                            Boolean boolean2 = Boolean.parseBoolean(v2.toString());
                            return ASC_order ? boolean1.compareTo(boolean2) : boolean2.compareTo(boolean1);
                        case TIMESTAMP:
                            Timestamp timestamp1 = (Timestamp) (v1);
                            Timestamp timestamp2 = (Timestamp) (v2);
                            return ASC_order ? timestamp1.compareTo(timestamp2) : timestamp2.compareTo(timestamp1);
                        default:
                            //调用对象的compareTo()方法比较大小
                            Method method = field.getType().getDeclaredMethod("compareTo", new Class[]{field.getType()});
                            method.setAccessible(true);
                            int result = (Integer) method.invoke(v1, new Object[]{v2});
                            return ASC_order ? result : result * (-1);
                    }
                } catch (Exception e) {
                    log.error("fail to  methodStr:{} cause:{}", newStr, e);
                }
                return 0;
            }
        });
        return list;
    }

    public static int compareValues(Object value1, Object value2, boolean ASC_order) {
        if (value1 == null && value2 == null) {
            return 0;
        } else if (value1 == null) {
            return 1;
        } else if (value2 == null) {
            return -1;
        }
        // 处理不同类型的比较
        if (objConvertInteger(value1) && objConvertInteger(value2)) {
            Integer num1 = intValueConvert(value1);
            Integer num2 = intValueConvert(value2);
            return ASC_order ? num1.compareTo(num2) : num2.compareTo(num1);
        } else if (objConvertLong(value1) && objConvertLong(value2)) {
            Long num1 = longValueConvert(value1);
            Long num2 = longValueConvert(value2);
            return ASC_order ? num1.compareTo(num2) : num2.compareTo(num1);
        } else if (objConvertDouble(value1) && objConvertDouble(value2)) {
            Double num1 = doubleValueConvert(value1);
            Double num2 = doubleValueConvert(value2);
            return ASC_order ? num1.compareTo(num2) : num2.compareTo(num1);
        } else if (objConvertDate(value1) && objConvertDate(value2)) {
            Date num1 = (Date) (value1);
            Date num2 = (Date) (value2);
            return ASC_order ? num1.compareTo(num2) : num2.compareTo(num1);
        } else if (objConvertBoolean(value1) && objConvertBoolean(value2)) {
            Boolean num1 = booleanValueConvert(value1);
            Boolean num2 = booleanValueConvert(value2);
            return ASC_order ? num1.compareTo(num2) : num2.compareTo(num1);
        } else if (objConvertTimestamp(value1) && objConvertTimestamp(value2)) {
            Timestamp num1 = (Timestamp) (value1);
            Timestamp num2 = (Timestamp) (value2);
            return ASC_order ? num1.compareTo(num2) : num2.compareTo(num1);
        }
        // 如果无法确定比较类型，按字符串比较
        return ASC_order ? strValueConvert(value1).compareTo(strValueConvert(value2)) : strValueConvert(value2).compareTo(strValueConvert(value1));
    }


    public static <T> void sortListByTypeOrder(List<T> dataList, String orderField, List<String> typeOrder) {
        sortListMethod(dataList, orderField, typeOrder, null);
    }

    public static <T> void sortListByTypeOrder(List<T> dataList, String orderField, List<String> typeOrder, String sortField) {
        sortListMethod(dataList, orderField, typeOrder, sortField);
    }

    /**
     * 给定排序参照typeOrder，按照指定的字段methodName排序
     *
     * @param dataList   原数据
     * @param orderField 指定字段
     * @param typeOrder  指定参照的list
     * @param sortField  第二级排序字段
     * @param <T>
     */
    // 泛型排序方法
    public static <T> void sortListMethod(List<T> dataList, String orderField, List<String> typeOrder, String sortField) {
        if (CollectionUtils.isEmpty(typeOrder) || CollectionUtils.isEmpty(dataList)) {
            return;
        }
        Comparator<T> comparator = (t1, t2) -> {
            try {
                Field field1 = null;
                Field field2 = null;
                try {
                    field1 = t1.getClass().getDeclaredField(orderField);
                } catch (NoSuchFieldException e) {
                    field1 = t1.getClass().getSuperclass().getDeclaredField(orderField);
                }
                try {
                    field2 = t2.getClass().getDeclaredField(orderField);
                } catch (NoSuchFieldException e) {
                    field2 = t2.getClass().getSuperclass().getDeclaredField(orderField);
                }
                field1.setAccessible(true);
                field2.setAccessible(true);
                String type1 = (String) field1.get(t1);
                String type2 = (String) field2.get(t2);
                int index1 = typeOrder.indexOf(type1);
                int index2 = typeOrder.indexOf(type2);
                int compare = Integer.compare(index1, index2);
                if (!Strings.isNullOrEmpty(sortField)) {
                    if (compare != 0) {
                        return compare;
                    } else {
                        Field sortField1 = null;
                        Field sortField2 = null;
                        try {
                            sortField1 = t1.getClass().getDeclaredField(sortField);
                        } catch (NoSuchFieldException e) {
                            sortField1 = t1.getClass().getSuperclass().getDeclaredField(sortField);
                        }
                        try {
                            sortField2 = t2.getClass().getDeclaredField(sortField);
                        } catch (NoSuchFieldException e) {
                            sortField2 = t2.getClass().getSuperclass().getDeclaredField(sortField);
                        }
                        sortField1.setAccessible(true);
                        sortField2.setAccessible(true);
                        Integer name1 = (Integer) (sortField1.get(t1));
                        Integer name2 = (Integer) (sortField2.get(t2));
                        if (name1 == null && name2 == null) {
                            return 0;
                        }
                        if (name1 == null) {
                            return 1;
                        }
                        if (name2 == null) {
                            return -1;
                        }
                        return Integer.compare(name1, name2);
                    }
                }
                return compare;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        };
        Collections.sort(dataList, comparator);
    }


    private static boolean objConvertDouble(Object obj) {
        if (obj instanceof Double) {
            return true;
        }
        return false;
    }

    private static boolean objConvertLong(Object obj) {
        if (obj instanceof Long) {
            return true;
        }
        return false;
    }

    private static boolean objConvertInteger(Object obj) {
        if (obj instanceof Integer) {
            return true;
        }
        return false;
    }

    private static boolean objConvertString(Object obj) {
        if (obj instanceof String) {
            return true;
        }
        return false;
    }

    private static boolean objConvertByte(Object obj) {
        if (obj instanceof Byte) {
            return true;
        }
        return false;
    }

    private static boolean objConvertChar(Object obj) {
        if (obj instanceof Character) {
            return true;
        }
        return false;
    }

    private static boolean objConvertDate(Object obj) {
        if (obj instanceof Date) {
            return true;
        }
        return false;
    }

    private static boolean objConvertFloat(Object obj) {
        if (obj instanceof Float) {
            return true;
        }
        return false;
    }

    private static boolean objConvertTimestamp(Object obj) {
        if (obj instanceof Timestamp) {
            return true;
        }
        return false;
    }

    private static boolean objConvertBoolean(Object obj) {
        if (obj instanceof Boolean) {
            return true;
        }
        return false;
    }

    private static boolean objConvertShort(Object obj) {
        if (obj instanceof Short) {
            return true;
        }
        return false;
    }


}
